import type { Metadata } from 'next'; import Link from 'next/link'; import { notFound } from 'next/navigation'; import { ExternalLink } from 'lucide-react'; import { GraphLink } from '@/components/graph/graph-link'; import { PageHeader, Section, KV, Note } from '@/components/ui/section'; import { Badge, ClaimBadge } from '@/components/ui/badge'; import { EmptyState } from '@/components/ui/empty-state'; import { Freshness } from '@/components/ui/freshness'; import { Pager } from '@/components/ui/pager'; import { SourceBadge } from '@/components/ui/source-badge'; import { ApprovalsTable } from '@/components/data/approvals-table'; import { EvidenceTable } from '@/components/data/evidence-table'; import { TrialTable } from '@/components/data/trial-list'; import { getDrugBySlug, approvalsForDrug, codesForDrug, pipelineForDrug, type DrugCodeRow } from '@/lib/queries/drugs'; import { evidenceForDrug, evidenceForDrugCount, EVIDENCE_PAGE_SIZE } from '@/lib/queries/evidence'; import { trialsForDrug, trialsForDrugCount, TRIAL_PAGE_SIZE } from '@/lib/queries/trials'; import { loadProvenance } from '@/lib/queries/provenance'; import { jsonLd, drugLd } from '@/lib/seo'; import { fmtDate, fmtInt, humanize, phaseLabel } from '@/lib/format'; import { pageInfo } from '@/lib/pagination'; import { str, int, withParams, type SP } from '@/lib/search-params'; export const revalidate = 3600; const CODE_SYSTEMS: Record = { atc: 'ATC (WHO)', din: 'DIN (Health Canada)', hc_drug_code: 'DPD drug code', unii: 'UNII (FDA GSRS)', rxcui: 'RxCUI (RxNorm)', ncit: 'NCIt', chembl: 'ChEMBL', drugbank: 'DrugBank', pubchem_cid: 'PubChem CID', civic_therapy: 'CIViC therapy', ema_product: 'EMA product', mhra: 'MHRA', tga: 'TGA', }; function codeSystemLabel(system: string): string { return CODE_SYSTEMS[system] ?? humanize(system); } /** External link for code systems with a stable public URL pattern; plain text otherwise. */ function CodeLink({ c, codes }: { c: DrugCodeRow; codes: DrugCodeRow[] }) { let url: string | null = null; if (c.system === 'atc') url = `https://atcddd.fhi.no/atc_ddd_index/?code=${encodeURIComponent(c.code)}`; else if (c.system === 'hc_drug_code') url = `https://health-products.canada.ca/dpd-bdpp/info?lang=eng&code=${encodeURIComponent(c.code)}`; else if (c.system === 'din') { // The DPD public page is keyed by drug code, not DIN: reuse the hc_drug_code row that carries the same brand label. const dc = codes.find((k) => k.system === 'hc_drug_code' && k.label === c.label); url = dc ? `https://health-products.canada.ca/dpd-bdpp/info?lang=eng&code=${encodeURIComponent(dc.code)}` : null; } else if (c.system === 'chembl') url = `https://www.ebi.ac.uk/chembl/compound_report_card/${encodeURIComponent(c.code)}/`; else if (c.system === 'ncit') url = `https://evsexplore.semantics.cancer.gov/evsexplore/concept/ncit/${encodeURIComponent(c.code)}`; else if (c.system === 'pubchem_cid') url = `https://pubchem.ncbi.nlm.nih.gov/compound/${encodeURIComponent(c.code)}`; else if (c.system === 'drugbank') url = `https://go.drugbank.com/drugs/${encodeURIComponent(c.code)}`; if (!url) return <>{c.code}; return ( {c.code} ); } export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise { const d = await getDrugBySlug((await params).slug); return d ? { title: `${d.name} — drug`, description: d.description ?? `${d.name}: regulatory approvals by jurisdiction, curated evidence and clinical trials.`, alternates: { canonical: `/drug/${d.slug}` } } : { title: 'Drug' }; } export default async function DrugPage({ params, searchParams }: { params: Promise<{ slug: string }>; searchParams: Promise }) { const { slug } = await params; const d = await getDrugBySlug(slug); if (!d) notFound(); const sp = await searchParams; const [evTotal, tTotal] = await Promise.all([evidenceForDrugCount(d.id), trialsForDrugCount(d.id)]); const ev = pageInfo(int(sp, 'evPage', 1, 1, 100_000), EVIDENCE_PAGE_SIZE, evTotal); const tp = pageInfo(int(sp, 'tPage', 1, 1, 100_000), TRIAL_PAGE_SIZE, tTotal); const aPage = int(sp, 'aPage', 1, 1, 100_000); const [approvals, evidence, trials, codes, pipeline] = await Promise.all([approvalsForDrug(d.id), evTotal ? evidenceForDrug(d.id, { page: ev.page, pageSize: ev.pageSize }) : Promise.resolve([]), tTotal ? trialsForDrug(d.id, { page: tp.page, pageSize: tp.pageSize }) : Promise.resolve([]), codesForDrug(d.id), pipelineForDrug(d.id)]); const hasCanada = approvals.some((a) => a.jurisdiction === 'CA'); const prov = await loadProvenance([...approvals.map((a) => a.provenance_id), ...evidence.map((e) => e.provenance_id)]); const jurisdictions = [...new Set(approvals.map((a) => a.jurisdiction))].sort(); const wanted = approvals.length ? str(sp, 'jurisdiction') : ''; const selected = jurisdictions.includes(wanted) ? wanted : null; const shown = selected ? approvals.filter((a) => a.jurisdiction === selected) : approvals; const aliases = d.aliases ?? []; const current = { jurisdiction: selected ?? '', evPage: ev.page > 1 ? ev.page : '', tPage: tp.page > 1 ? tp.page : '', aPage: aPage > 1 ? aPage : '' }; const href = (o: Record, hash?: string) => `/drug/${d.slug}${withParams(current, o)}${hash ? `#${hash}` : ''}`; return (